View Javadoc
1   package edu.jiangxin.apktoolbox.file.batchrename;
2   
3   import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
4   import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5   import edu.jiangxin.apktoolbox.utils.Constants;
6   import org.apache.commons.io.FileUtils;
7   import org.apache.commons.io.FilenameUtils;
8   import org.apache.commons.lang3.StringUtils;
9   
10  import javax.swing.*;
11  import java.awt.*;
12  import java.awt.event.ActionEvent;
13  import java.awt.event.ActionListener;
14  import java.io.File;
15  import java.io.IOException;
16  import java.nio.file.Files;
17  import java.nio.file.Path;
18  import java.util.Collection;
19  import java.util.Set;
20  import java.util.TreeSet;
21  
22  //https://www.zhihu.com/question/50890909
23  public class BatchRenamePanel extends EasyPanel {
24      private static final String STRING_TYPE_PARENT_NAME = "使用父目录名";
25  
26      private static final String STRING_TYPE_PARENT_PATH = "使用目录路径";
27  
28      private static final String STRING_TYPE_SPECIALISE_STRING = "使用指定字符";
29  
30      private JPanel warningPanel;
31  
32      private JPanel sourcePanel;
33  
34      private JPanel rulePanel;
35  
36      private JPanel operationPanel;
37  
38      private JPanel statusPanel;
39  
40      private JTextField sourceTextField;
41      private JTextField suffixTextField;
42      private JCheckBox recursiveCheckBox;
43      private JRadioButton ruleRadioButton1;
44      private JRadioButton ruleRadioButton2;
45      private JRadioButton ruleRadioButton3;
46      private JRadioButton ruleRadioButton4;
47      private JRadioButton ruleRadioButton5;
48      private JRadioButton ruleRadioButton6;
49      private JTextField textField21;
50      private JSpinner spinner21;
51      private JCheckBox checkBox31;
52      private JCheckBox checkBox32;
53      private JCheckBox checkBox33;
54      private JTextField textField31;
55      private JTextField textField32;
56      private JSpinner spinner31;
57      private JSpinner spinner32;
58      private JSpinner spinner41;
59      private JComboBox<String> comboBoxStringType;
60      private JTextField textField43;
61      private JSpinner spinner51;
62      private JSpinner spinner52;
63      private JTextField textField61;
64      private JTextField textField62;
65      private JTextField currentTextField;
66  
67      public BatchRenamePanel() throws HeadlessException {
68          super();
69      }
70  
71      @Override
72      public void initUI() {
73          BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
74          setLayout(boxLayout);
75  
76          createWarningPanel();
77          add(warningPanel);
78          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
79  
80          createSourcePanel();
81          add(sourcePanel);
82          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
83  
84          createRulePanel();
85          add(rulePanel);
86          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
87  
88          createOperationPanel();
89          add(operationPanel);
90          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
91  
92          createStatusPanel();
93          add(statusPanel);
94      }
95  
96      private void createWarningPanel() {
97          warningPanel = new JPanel();
98          warningPanel.setLayout(new BoxLayout(warningPanel, BoxLayout.X_AXIS));
99  
100         JTextArea warningTextArea = new JTextArea();
101         warningTextArea.setText("It is dangerous to operate on the original files! \nPlease back up at first and check the result carefully at end!");
102         warningTextArea.setForeground(Color.RED);
103         warningTextArea.setFont(new Font("宋体", Font.BOLD, 20));
104         warningTextArea.setBorder(null);
105         warningTextArea.setEditable(false);
106 
107         warningPanel.add(warningTextArea);
108         warningPanel.add(Box.createHorizontalGlue());
109     }
110 
111     private void createSourcePanel() {
112         sourcePanel = new JPanel();
113         sourcePanel.setBorder(BorderFactory.createTitledBorder("1. 源文件"));
114         sourcePanel.setLayout(new BorderLayout());
115 
116         JPanel secondLevelPanel = new JPanel();
117         secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
118         sourcePanel.add(secondLevelPanel);
119 
120         JPanel thirdLevelPanel1 = new JPanel();
121         thirdLevelPanel1.setLayout(new BoxLayout(thirdLevelPanel1, BoxLayout.X_AXIS));
122 
123         JPanel thirdLevelPanel2 = new JPanel();
124         thirdLevelPanel2.setLayout(new BoxLayout(thirdLevelPanel2, BoxLayout.X_AXIS));
125 
126         secondLevelPanel.add(thirdLevelPanel1);
127         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
128         secondLevelPanel.add(thirdLevelPanel2);
129 
130         JLabel label = new JLabel("源文件所在目录:");
131         sourceTextField = new JTextField();
132         JButton button = new JButton("选择");
133         button.addActionListener(new SelectDirectoryListener("Select a directory", sourceTextField));
134         thirdLevelPanel1.add(label);
135         thirdLevelPanel1.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
136         thirdLevelPanel1.add(sourceTextField);
137         thirdLevelPanel1.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
138         thirdLevelPanel1.add(button);
139 
140         JLabel label2 = new JLabel("后缀:");
141         suffixTextField = new JTextField();
142         suffixTextField.setToolTipText("an array of extensions, ex. java,xml,mp4. If this parameter is empty, all files are returned.");
143         recursiveCheckBox = new JCheckBox("包括子目录");
144 
145         thirdLevelPanel2.add(label2);
146         thirdLevelPanel2.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
147         thirdLevelPanel2.add(suffixTextField);
148         thirdLevelPanel2.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
149         thirdLevelPanel2.add(recursiveCheckBox);
150     }
151 
152     private void createRulePanel() {
153         rulePanel = new JPanel();
154         rulePanel.setBorder(BorderFactory.createTitledBorder("3. 更名规则"));
155         rulePanel.setLayout(new BorderLayout());
156 
157         JPanel secondLevelPanel = new JPanel();
158         secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
159         rulePanel.add(secondLevelPanel);
160 
161         JPanel thirdLevelPanel1 = new JPanel();
162         thirdLevelPanel1.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
163 
164         JPanel thirdLevelPanel2 = new JPanel();
165         thirdLevelPanel2.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
166 
167         JPanel thirdLevelPanel3 = new JPanel();
168         thirdLevelPanel3.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
169 
170         JPanel thirdLevelPanel4 = new JPanel();
171         thirdLevelPanel4.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
172 
173         JPanel thirdLevelPanel5 = new JPanel();
174         thirdLevelPanel5.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
175 
176         JPanel thirdLevelPanel6 = new JPanel();
177         thirdLevelPanel6.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
178 
179         secondLevelPanel.add(thirdLevelPanel1);
180         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
181         secondLevelPanel.add(thirdLevelPanel2);
182         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
183         secondLevelPanel.add(thirdLevelPanel3);
184         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
185         secondLevelPanel.add(thirdLevelPanel4);
186         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
187         secondLevelPanel.add(thirdLevelPanel5);
188         secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
189         secondLevelPanel.add(thirdLevelPanel6);
190 
191         ButtonGroup buttonGroup = new ButtonGroup();
192 
193         {
194             ruleRadioButton1 = new JRadioButton("只在文件重名时才更名");
195             ruleRadioButton1.setSelected(true);
196             thirdLevelPanel1.add(ruleRadioButton1);
197             buttonGroup.add(ruleRadioButton1);
198         }
199 
200         {
201             ruleRadioButton2 = new JRadioButton("将前缀{String}后的数字改为{Int}位");
202             thirdLevelPanel2.add(ruleRadioButton2);
203             buttonGroup.add(ruleRadioButton2);
204 
205             JLabel label21 = new JLabel("String");
206             textField21 = new JTextField("");
207             JLabel label22 = new JLabel("Int");
208             spinner21 = new JSpinner();
209             spinner21.setValue(4);
210             thirdLevelPanel2.add(label21);
211             thirdLevelPanel2.add(textField21);
212             thirdLevelPanel2.add(label22);
213             thirdLevelPanel2.add(spinner21);
214         }
215 
216         {
217             ruleRadioButton3 = new JRadioButton("重编号所有文件");
218             thirdLevelPanel3.add(ruleRadioButton3);
219             buttonGroup.add(ruleRadioButton3);
220 
221             checkBox31 = new JCheckBox("编号先按原文件名排序");
222             checkBox32 = new JCheckBox("按末尾数字排序");
223             checkBox33 = new JCheckBox("用目录名为前缀");
224             JLabel label31 = new JLabel("分隔字符");
225             textField31 = new JTextField("_");
226             JLabel label32 = new JLabel("前缀");
227             textField32 = new JTextField("File");
228             JLabel label33 = new JLabel("起始号码");
229             spinner31 = new JSpinner();
230             spinner31.setValue(1);
231             JLabel label34 = new JLabel("号码位数");
232             spinner32 = new JSpinner();
233             spinner32.setValue(3);
234             thirdLevelPanel3.add(checkBox31);
235             thirdLevelPanel3.add(checkBox32);
236             thirdLevelPanel3.add(checkBox33);
237             thirdLevelPanel3.add(label31);
238             thirdLevelPanel3.add(textField31);
239             thirdLevelPanel3.add(label32);
240             thirdLevelPanel3.add(textField32);
241             thirdLevelPanel3.add(label33);
242             thirdLevelPanel3.add(spinner31);
243             thirdLevelPanel3.add(label34);
244             thirdLevelPanel3.add(spinner32);
245         }
246 
247         {
248             ruleRadioButton4 = new JRadioButton("在第{Int}个字符后添加{String}");
249             thirdLevelPanel4.add(ruleRadioButton4);
250             buttonGroup.add(ruleRadioButton4);
251 
252             JLabel stringType = new JLabel("字符串类型");
253 
254             comboBoxStringType = new JComboBox<>();
255             comboBoxStringType.addItem(STRING_TYPE_PARENT_NAME);
256             comboBoxStringType.addItem(STRING_TYPE_PARENT_PATH);
257             comboBoxStringType.addItem(STRING_TYPE_SPECIALISE_STRING);
258             comboBoxStringType.setSelectedItem(STRING_TYPE_SPECIALISE_STRING);
259             comboBoxStringType.addItemListener(e -> textField43.setEditable(e.getItem().equals(STRING_TYPE_SPECIALISE_STRING)));
260 
261             JLabel label41 = new JLabel("Int");
262             spinner41 = new JSpinner();
263             spinner41.setValue(0);
264 
265             JLabel label43 = new JLabel("String");
266             textField43 = new JTextField("");
267 
268             thirdLevelPanel4.add(label41);
269             thirdLevelPanel4.add(spinner41);
270             thirdLevelPanel4.add(label43);
271             thirdLevelPanel4.add(textField43);
272             thirdLevelPanel4.add(stringType);
273             thirdLevelPanel4.add(comboBoxStringType);
274         }
275 
276         {
277             ruleRadioButton5 = new JRadioButton("删除自第{Int:1}个字符开始的{Int:2}个字符");
278             thirdLevelPanel5.add(ruleRadioButton5);
279             buttonGroup.add(ruleRadioButton5);
280 
281             JLabel label51 = new JLabel("Int:1");
282             spinner51 = new JSpinner();
283             spinner51.setValue(1);
284             JLabel label52 = new JLabel("Int:2");
285             spinner52 = new JSpinner();
286             spinner52.setValue(1);
287             thirdLevelPanel5.add(label51);
288             thirdLevelPanel5.add(spinner51);
289             thirdLevelPanel5.add(label52);
290             thirdLevelPanel5.add(spinner52);
291         }
292 
293         {
294             ruleRadioButton6 = new JRadioButton("将文件名中出现的字符串{String:1}替换为{String:2}");
295             thirdLevelPanel6.add(ruleRadioButton6);
296             buttonGroup.add(ruleRadioButton6);
297 
298             JLabel label61 = new JLabel("String:1");
299             textField61 = new JTextField("");
300             JLabel label62 = new JLabel("String:2");
301             textField62 = new JTextField("");
302             thirdLevelPanel6.add(label61);
303             thirdLevelPanel6.add(textField61);
304             thirdLevelPanel6.add(label62);
305             thirdLevelPanel6.add(textField62);
306         }
307     }
308 
309     private void createOperationPanel() {
310         operationPanel = new JPanel();
311         operationPanel.setBorder(BorderFactory.createTitledBorder("6. 操作"));
312         operationPanel.setLayout(new BorderLayout());
313 
314         JPanel secondLevelPanel = new JPanel();
315         secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.X_AXIS));
316         operationPanel.add(secondLevelPanel);
317 
318         JButton button = new JButton("开始");
319         button.addActionListener(new StartButtonActionListener());
320         secondLevelPanel.add(button);
321         secondLevelPanel.add(Box.createHorizontalGlue());
322     }
323 
324     private void createStatusPanel() {
325         statusPanel = new JPanel();
326         statusPanel.setBorder(BorderFactory.createTitledBorder("正在处理"));
327         statusPanel.setLayout(new BorderLayout());
328 
329         JPanel secondLevelPanel = new JPanel();
330         secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
331         statusPanel.add(secondLevelPanel);
332 
333         currentTextField = new JTextField();
334         secondLevelPanel.add(currentTextField);
335     }
336 
337     private final class StartButtonActionListener implements ActionListener {
338         @Override
339         public void actionPerformed(ActionEvent e) {
340             String sourceString = sourceTextField.getText();
341             if (StringUtils.isEmpty(sourceString)) {
342                 JOptionPane.showMessageDialog(BatchRenamePanel.this, "sourceString is empty", "ERROR",
343                         JOptionPane.ERROR_MESSAGE);
344                 return;
345             }
346             File sourceFile = new File(sourceString);
347             if (!sourceFile.exists() || !sourceFile.isDirectory()) {
348                 JOptionPane.showMessageDialog(BatchRenamePanel.this, "sourceFile does not exist or not directory", "ERROR",
349                         JOptionPane.ERROR_MESSAGE);
350                 return;
351             }
352             String[] extensions = null;
353             if (StringUtils.isNotEmpty(suffixTextField.getText())) {
354                 extensions = suffixTextField.getText().split(",");
355             }
356             Collection<File> fileCollection = FileUtils.listFiles(sourceFile, extensions, recursiveCheckBox.isSelected());
357             Set<File> fileSet = new TreeSet<>(fileCollection);
358             if (fileSet.isEmpty()) {
359                 JOptionPane.showMessageDialog(BatchRenamePanel.this, "fileSet is empty", "ERROR",
360                         JOptionPane.ERROR_MESSAGE);
361                 return;
362             }
363             for (File file : fileSet) {
364                 renameSingleFile(file);
365             }
366         }
367     }
368 
369     private void renameSingleFile(File file) {
370         String sourceFile = FilenameUtils.normalizeNoEndSeparator(file.getAbsolutePath());
371         String sourceFileName = FilenameUtils.getName(sourceFile);
372         String currentDir = FilenameUtils.getPath(sourceFile);
373         String targetFileName = "";
374         if (ruleRadioButton1.isSelected()) {
375             logger.info("unimplemented");
376         } else if (ruleRadioButton2.isSelected()) {
377             targetFileName = getNewFileNameWhen2Selected(sourceFileName);
378         } else if (ruleRadioButton3.isSelected()) {
379             targetFileName = getNewFileNameWhen3Selected();
380         } else if (ruleRadioButton4.isSelected()) {
381             targetFileName = getNewFileNameWhen4Selected(file);
382         } else if (ruleRadioButton5.isSelected()) {
383             targetFileName = getNewFileNameWhen5Selected(sourceFileName);
384         } else if (ruleRadioButton6.isSelected()) {
385             targetFileName = getNewFileNameWhen6Selected(sourceFileName);
386         } else {
387             logger.error("ruleRadioButton select status error");
388             return;
389         }
390 
391         try {
392             Path sourcePath = new File(sourceFile).toPath();
393             Files.move(sourcePath, sourcePath.resolveSibling(targetFileName));
394             StringBuilder sb = new StringBuilder();
395             sb.append(currentDir).append(" [").append(sourceFileName).append("]->[").append(targetFileName).append("]");
396             currentTextField.setText(sb.toString());
397             logger.info(sb.toString());
398         } catch (IOException e) {
399             logger.error("copy file failed: IOException. current: " + sourceFile);
400         }
401     }
402 
403     private String getNewFileNameWhen2Selected(String oldFileName) {
404         String prefix = textField21.getText();
405         String sourceFileNameWithoutSuffix = FilenameUtils.getBaseName(oldFileName);
406         String serialNumberStr = StringUtils.substringAfter(sourceFileNameWithoutSuffix, prefix);
407         int serialNumber = Integer.parseInt(serialNumberStr);
408         int digit = (Integer) spinner21.getValue();
409         String format = "%0" + digit + "d";
410         return oldFileName.replaceAll(serialNumberStr, String.format(format, serialNumber));
411     }
412 
413     private String getNewFileNameWhen3Selected() {
414         String separator = textField31.getText();
415         String prefix = textField32.getText();
416         int start = (Integer) spinner31.getValue();
417         int digit = (Integer) spinner32.getValue();
418         boolean isCheckBox31Selected = checkBox31.isSelected();
419         boolean isCheckBox32Selected = checkBox32.isSelected();
420         boolean isCheckBox33Selected = checkBox33.isSelected();
421         logger.info("unimplemented: separator: {}, prefix: {}, start: {}, digit: {}", separator, prefix, start, digit);
422         logger.info("unimplemented: isCheckBox31Selected: {}, isCheckBox32Selected: {}, isCheckBox33Selected: {}",
423                 isCheckBox31Selected, isCheckBox32Selected, isCheckBox33Selected);
424         return "";
425     }
426 
427     private String getNewFileNameWhen4Selected(File file) {
428         String sourceFile = FilenameUtils.normalizeNoEndSeparator(file.getAbsolutePath());
429         String sourceFileName = FilenameUtils.getName(sourceFile);
430         int index = (Integer) spinner41.getValue();
431         String insertString = "";
432         switch ((String) comboBoxStringType.getSelectedItem()) {
433             case STRING_TYPE_PARENT_PATH:
434                 break;
435             case STRING_TYPE_PARENT_NAME:
436                 String parentFile = FilenameUtils.normalizeNoEndSeparator(file.getParentFile().getAbsolutePath());
437                 insertString = FilenameUtils.getName(parentFile);
438                 break;
439             case STRING_TYPE_SPECIALISE_STRING:
440                 insertString = textField43.getText();
441                 break;
442         }
443         return StringUtils.substring(sourceFileName, 0, index) + insertString + StringUtils.substring(sourceFileName, index);
444     }
445 
446     private String getNewFileNameWhen5Selected(String oldFileName) {
447         int start = (Integer) spinner51.getValue();
448         int length = (Integer) spinner52.getValue();
449         return StringUtils.substring(oldFileName, 0, start - 1) + StringUtils.substring(oldFileName, start + length - 1);
450     }
451 
452     private String getNewFileNameWhen6Selected(String oldFileName) {
453         return StringUtils.replace(oldFileName, textField61.getText(), textField62.getText());
454     }
455 }